(function() {
  'use strict';
  var ns = window.beneath = window.beneath || {};
  
  function bellFn(x) {
    var a = 1.0;
    var b = 1.5;
    return 1 / (1 + Math.exp(Math.abs((x - 10) / a), 2.0 * b));
  }
  function Tremor(game) {
    var sx = Math.random() * game.world.width / 3;
    var gy = Math.random() * game.world.height / 3;


    var rocks = [];
    for(var i = 0 ; i < 50 ; i++) {
      var ggy = gy + (Math.random() - 0.5) * 100;
      var ssx = sx + (Math.random() - 0.5) * 100;

      var spr = game.tremorGroup.create(ssx, 0 - Math.random() * 100, 'sml', _.sample([55, 56, 57, 58]));
      spr.vy = Math.random() * 5;
      spr.damp = -0.24;
      spr.dy = 0.1;
      spr.ggy = ggy;
      spr.deg = 0.95;

      rocks.push(spr);
    }

    setTimeout(function () {
      for(var i = 0 ; i < 50 ; i++) {
        var ggy = gy + (Math.random() - 0.5) * 100;
        var ssx = sx + (Math.random() - 0.5) * 100;

        var spr = game.tremorGroup.create(ssx, 0 - Math.random() * 100, 'sml', _.sample([51, 52, 53, 54]));
        spr.vy = Math.random() * 5;
        spr.damp = -0.75;
        spr.dy = 0.1;
        spr.ggy = ggy;
        spr.deg = 0.99;

        rocks.push(spr);
      }

      setTimeout(function () {
        for(var i = 0 ; i < game.miners.length ; i ++) {
          var miner = game.miners[i];

          var dx = miner.x - sx;
          var dy = miner.y - gy;
          var dist = dx * dx + dy * dy;
          dist = Math.sqrt(dist);
          console.log(dist);

          if(dist < 50) {
            var deadMiner = game.miners.splice(i, 1)[0];
            i -= 1;

            deadMiner.kill();

            var xx = game.state.employees.indexOf(deadMiner.stats);
            game.state.employees.splice(xx, 1);
            game.state.minerDied.push([deadMiner.stats]);
          }

          if(dist >= 50 && dist < 150) {
            miner.stats.morale -= 50;
            miner.ideas.push('sue');
          }
        }
      }, 500);
    }, 5000 + 5000 * Math.random() | 0);

    this.update = function () {
      for (var i = 0; i < rocks.length; i++) {
        var rock = rocks[i];
        rock.y += rock.vy;
        rock.vy += rock.dy;
        rock.alpha *= rock.deg;

        if(rock.y > rock.ggy) {
          rock.vy *= 0.0;
          rock.dy *= 0.0;
        }
      }
    };
  }


  function randomMap(w, h) {
    var out = ns.createMap(w, h);
    var n = new ns.noise();

    for(var x = 0 ; x < w ; x ++) {
      for(var y = 0 ; y < h ; y ++) {
        var wall = n.noise(x * 0.5, y * 0.5) > -0.5;
        var coal = n.noise(x * 0.1 + 1000, y * 0.5 + 100) < 0.0;
        if(wall && coal) {
          out.set(new ns.walls.CoalWall(), x, y);
        }
        if(wall && !coal) {
          out.set(new ns.walls.StoneWall(), x, y);
        }
      }
    }
    return out;
  }
  function forEach(w, h, fun) {
    for(var x = 0 ; x < w ; x ++) {
      for(var y = 0 ; y < h ; y ++) {
        fun(x, y);
      }
    }
  }


  function Game() {
  }

  
  function Clock(game){
    var inTimeSpeed = 0.001;
    var radius = 100;
    var inTime = 0.0;


    var moon = game.add.sprite(0, 0, 'gfx', 6 * 32);
    moon.anchor.x = 0.5;
    moon.anchor.y = 0.5;

    var sun = game.add.sprite(0, 0, 'gfx', 6 * 32 + 1);
    sun.anchor.x = 0.5;
    sun.anchor.y = 0.5;
    

    this.update = function() {
      inTime += inTimeSpeed;
      inTime = Math.min(inTime, 1.0);

      moon.alpha = inTime;
      sun.alpha = inTime;

      moon.bringToTop();
      sun.bringToTop();
      var a0 = (-game.timeRemaining / game.totalTime) * Math.PI - 0.75 + Math.PI;
      var a1 = (-game.timeRemaining / game.totalTime) * Math.PI  + Math.PI;

      var c0 = Math.cos(a0);
      var s0 = Math.sin(a0);

      var c1 = Math.cos(a1);
      var s1 = Math.sin(a1);

      moon.x = game.world.centerX / 3 + c0 * radius;
      moon.y = game.world.height / 3 + s0 * radius * 0.3;
      sun.x = game.world.centerX / 3 + c1 * -radius;
      sun.y = game.world.height / 3 + s1 * -radius * 0.3;
    };
  }


  Game.prototype = {
    playHit : function  () {
      var self = this;

      _.delay(function () {
        _.sample(self.hitSounds).play();
      }, 1000);
    },
    checkCollision : function  (spr) {
      function wallCollision(tile) {
        return {
          collidedWithWalls : true,
          wall : tile
        };
      }
      var xx0 = spr.x - spr.width * (spr.anchor.x);
      var yy0 = spr.y - spr.height * (spr.anchor.y);
      var xx1 = spr.x + spr.width * (1.0 - spr.anchor.x);
      var yy1 = spr.y + spr.height * (1.0 - spr.anchor.y);

      var x0 = this.walls.getTileX(xx0);
      var y0 = this.walls.getTileY(yy0);

      var x1 = this.walls.getTileX(xx1);
      var y1 = this.walls.getTileY(yy1);
      
      var tile = this.map.get(x0, y0);
      if(tile) {
        return wallCollision(tile);
      }
      tile = this.map.get(x1, y0);
      if(tile) {
        return wallCollision(tile);
      }
      tile = this.map.get(x0, y1);
      if(tile) {
        return wallCollision(tile);
      }
      tile = this.map.get(x1, y1);
      if(tile) {
        return wallCollision(tile);
      }

      if(x0 <= 0 || y0 <= 0 || x1 >= this.tilesX || y1 >= this.tilesY) {
        var dx = x0 <= 0 ? 1 : x1 >= this.tilesX ? -1 : 0;
        var dy = y0 <= 0 ? 1 : y1 >= this.tilesY ? -1 : 0;
        return {
          outOfBounds : true,
          dx : dx,
          dy : dy
        };
      }

      return {
        noCollision : true
      };
    },
    spawn: function spawnFn(x, y, stats) {
      var spr = new ns.Miner(this, x, y, stats).spr;

      this.entities.push(spr);
      this.miners.push(spr);

      this.character.add(spr);
    },

    createCoal : function createCoalFn(x, y){
      var spr = new ns.Coal(this, x, y).spr;

      this.entities.push(spr);
      this.coal.push(spr);

      this.projectile.add(spr);
    },

    createFontPart : function createFontPartFn(x, y, text, float){
      var spr = new ns.ParticleText(this.game, x, y, text, float).spr;
      this.entities.push(spr);

      this.textParticle.add(spr);
    },
    init : function (state) {
      this.state = state[0];


      this.coalCollected = 0; // Start 6 AM
      this.earlyMorning = 2 * 60 * 60;
      this.morning = 5 * 60 * 60;
      this.noon = 7 * 60 * 60;
      this.afternoon = 10 * 60 * 60;
      this.evening = 10 * 60 * 60;

      this.timeSpeed = 20;

      
      this.totalTime = 13 * 60 * 60; 


      this.day = state.day;

      this.timeRemaining = this.totalTime;

      this.getTimeOfDay = function () {
        var timePassed = this.totalTime - this.timeRemaining;
        if(timePassed < this.earlyMorning) return 'early morning';
        if(timePassed < this.morning) return 'morning';
        if(timePassed < this.noon) return 'noon';
        if(timePassed < this.afternoon) return 'afternoon';
        if(timePassed < this.evening) return 'evening';
        return 'night';
      };
    },
    create: function () {
      this.tremorInProgress = false;

      this.hitSounds = [this.add.audio('hit0'), this.add.audio('hit1'), this.add.audio('hit2')];

      this.breakSound = this.add.audio('break');
      this.coalSound = this.add.audio('coal');
      this.speakSound = this.add.audio('speak');

      this.groupBG = this.add.group();
      this.projectile = this.add.group();
      this.character = this.add.group();

      this.textParticle = this.add.group();
      this.tremorGroup = this.add.group();


      this.entities = [];
      this.coal = [];
      this.coalsCollected = [];
      this.miners = [];
      
      this.game.camera.scale.set(3.0, 3.0);
      

      var tilesX = this.tilesX = Math.floor(window.document.body.clientWidth / (16 * 3) + 1);
      var tilesY = this.tilesY = Math.floor(window.document.body.clientHeight / (19 * 3) + 1);


      var tilemap = this.tilemap = this.game.add.tilemap();
      tilemap.tileWidth = 16;
      tilemap.tileHeight = 19;


      var visited = this.visited = ns.createMap(tilesX, tilesY);
      
      tilemap.addTilesetImage('gfx');
      var background = this.background = tilemap.create('backgroundLayer', tilesX, tilesY, 16, 19);
      background.baseTexture.scaleMode = 1;

      var walls = this.walls = tilemap.createBlankLayer('foregroundLayer', tilesX, tilesY, 16, 19);
      walls.baseTexture.scaleMode = 1;

      this.groupBG.add(background);
      this.groupBG.add(walls);

      this.UIText = this.add.group();
      this.UI = this.add.group();

      this.clock = new Clock(this);
      this.dayText = 'Early Morning, day ' + 1;


      this.dayTextObj = this.add.bitmapText(0, this.world.height / 3 - 10, 'font', this.dayText, 5);
      this.dayTextObj.x = this.world.centerX / 3 - this.dayTextObj.textWidth / 2;
      this.UIText.add(this.dayTextObj);


      var map = this.map = randomMap(tilesX, tilesY);
      
      // Entrance
      forEach(tilesX, tilesY, function (x, y) {
        visited.set(new ns.FloorTile(), x, y);
      });

      forEach(tilesX, tilesY, function (x, y) {
        if(x + Math.abs(tilesY / 2 - y) < 3) {
          map.set(undefined, x, y);
        }
      });
      for(var i = 0 ; i < this.state.employees.length ; i ++) {
        var miner = this.state.employees[i];
        miner.morale += 10;
        this.spawn(2 * 16 * Math.random(), tilesY * 19 / 2, miner);
      }

    },
    stepOnFloor: function (x, y) {

      var x0 = this.background.getTileX(x);
      var y0 = this.background.getTileY(y);

      var t = this.visited.get(x0, y0);
      if(t === undefined) {
        return;
      }

      if(t.visitLevel === 0) {
        return;
      }

      var self = this;


      forEach(8, 8, function(dx, dy) {
        var x1 = x0 - (dx - 4), y1 = y0 - (dy - 4);

        if(x1 < 0 || y1 < 0 || x1 >= self.tilesX || y1 >= self.tilesY) {
          return;
        }

        var dist = Math.abs(x1 - x0) + Math.abs(y1 - y0);
        if(dist >= 4) {
          return;
        }
        dist = Math.floor(dist);

        var floorT = self.visited.get(x1, y1);

        if(!floorT) {
          return;
        }


        if(floorT.visitLevel === 0 || floorT.visitLevel < dist) {
          return;
        }
        floorT.visitLevel = dist;
        self.tilemap.putTile(14 * 32 + 32 * dist + floorT.index, x1, y1, self.background);

        var wallT = self.map.get(x1, y1);
        if(wallT) {
          var tile = self.tilemap.putTile(wallT.index + 2 * 32 + 32 * dist, x1, y1, self.walls);
          wallT.wallTile = tile;
          wallT.x = x1;
          wallT.y = y1;
        }
      });
    },

    update: function () {
      this.timeRemaining -= this.timeSpeed;

      for (var i = 0; i < this.entities.length; i++) {
        var spr = this.entities[i];
        if(!spr.exists) {
          this.entities.splice(i, 1);
          continue;
        }
        spr.entity.update();
      }

      for (var i = this.coal.length - 1; i >= 0; i--) {
        var coal = this.coal[i];

        for (var m = this.miners.length - 1; m >= 0; m--) {
          var miner = this.miners[m];

          if(coal.entity.near(miner.x + 8, miner.y + 9, 10)) {
            this.coalCollected += 1;
            this.coalSound.play();
            this.coalsCollected.push(coal.entity.index);
            coal.destroy();
            this.coal.splice(i, 1);

            this.createFontPart(miner.x, miner.y, '+ 1');
            break;
          }
        }
      }
      var self = this;
      forEach(this.tilesX, this.tilesY, function (x, y) {
        if(self.map.get(x, y) && self.map.get(x, y).health < 0) {
          self.tilemap.putTile(null, x, y, self.walls);
          self.breakSound.play();
          self.map.get(x, y).destroy(self, x, y);
          self.map.set(undefined, x, y);
        }
      });

      this.clock.update();

      this.dayTextObj.text = this.getTimeOfDay() + ' day ' + this.state.day;
      this.dayTextObj.x = this.world.centerX / 3 - this.dayTextObj.textWidth / 2;

      if(this.timeRemaining <= 0) {

        this.camera.scale.x = 1.0;
        this.camera.scale.y = 1.0;

        this.state.coal = this.coalsCollected;

        this.game.state.start('stats', true, false, [this.state]);
      }

      if(this.state.unstableArea && !this.tremorInProgress && Math.random() < 0.1) {
        this.startTremor();
      }

      if(this.tremor) {
        this.tremor.update();
      }
    },
    startTremor : function startTremorFn(){
      console.log('starting tremor!!');
      this.tremorInProgress = true;
      this.tremor = new Tremor(this);
    }
  };

  
  ns.Game = Game;

}());
